package net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.array;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import java.util.LinkedHashMap;
import java.util.Map;

import static org.objectweb.asm.Opcodes.BALOAD;
import static org.objectweb.asm.Opcodes.DALOAD;
import static org.objectweb.asm.Opcodes.FALOAD;
import static org.objectweb.asm.Opcodes.IALOAD;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.LALOAD;
import static org.objectweb.asm.Opcodes.SALOAD;

/**
 * @author daimao  Date: 16/4/21 Time: 下午11:22
 * @version $Id$
 */
public class PrimeArrayLoadAppliers {

    static final Map<Class<?>, BoxArrayType> primArrayTypeBoxInfoMap = new LinkedHashMap<>();

    static {
        primArrayTypeBoxInfoMap.put(byte[].class, new BoxArrayType(Byte.class, byte.class, BALOAD));
        primArrayTypeBoxInfoMap.put(short[].class, new BoxArrayType(Short.class, short.class, SALOAD));
        primArrayTypeBoxInfoMap.put(int[].class, new BoxArrayType(Integer.class, int.class, IALOAD));
        primArrayTypeBoxInfoMap.put(long[].class, new BoxArrayType(Long.class, long.class, LALOAD));
        primArrayTypeBoxInfoMap.put(float[].class, new BoxArrayType(Float.class, float.class, FALOAD));
        primArrayTypeBoxInfoMap.put(double[].class, new BoxArrayType(Double.class, double.class, DALOAD));
    }

    public static boolean isPrimeArrayType(Class<?> type) {
        return primArrayTypeBoxInfoMap.containsKey(type);
    }

    //return boxType
    public static Class<?> applyCode(Class<?> arrayType, MethodVisitor mv) throws Exception {
        BoxArrayType boxArrayType = primArrayTypeBoxInfoMap.get(arrayType);
        boxArrayType.applyCode(mv);
        return boxArrayType.boxType;
    }

    static class BoxArrayType {
        private final String boxTypeInternalName;
        private final String boxTypeDesc;
        private final String primTypeDesc;
        private final int loadIns;
        private final Class<?> boxType;
        private static final String valueOf = "valueOf";

        public BoxArrayType(Class<?> boxType, Class<?> primType, int loadIns) {
            this.boxType = boxType;
            boxTypeInternalName = Type.getInternalName(boxType);
            boxTypeDesc = Type.getDescriptor(boxType);
            primTypeDesc = Type.getDescriptor(primType);
            this.loadIns = loadIns;
        }

        void applyCode(MethodVisitor mv) {
            mv.visitInsn(loadIns);
            mv.visitMethodInsn(INVOKESTATIC, boxTypeInternalName, valueOf, "(" + primTypeDesc + ")" + boxTypeDesc, false);
        }
    }
}